/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.apisupport; import java.beans.*; import java.io.*; import java.util.*; import java.util.jar.Manifest; import javax.swing.SwingUtilities; import javax.swing.event.*; import javax.swing.text.*; import org.openide.actions.*; import org.openide.cookies.*; import org.openide.execution.*; import org.openide.filesystems.*; import org.openide.loaders.*; import org.openide.nodes.*; import org.openide.text.EditorSupport; import org.openide.text.NbDocument; import org.openide.util.HelpCtx; import org.openide.util.WeakListener; // [PENDING] for some reason, reload dialog appears frequently from these files // though there is no code which is actually writing to the file except via EditorSupport's // own Document! public class ManifestDataObject extends MultiDataObject { private ChangeListener chglist; private DocumentListener doclist; private static final long serialVersionUID =5300582282070962055L; public ManifestDataObject(FileObject pf, ManifestDataLoader loader) throws DataObjectExistsException { super (pf, loader); init (); } /* public Node.Cookie getCookie (Class clazz) { Node.Cookie cookie = super.getCookie (clazz); System.err.println("ManifestDataObject.getCookie: " + clazz + " -> " + cookie); return cookie; } */ private void init () { CookieSet cookies = getCookieSet (); cookies.add (new ManifestProvider.ModuleExecSupport (getPrimaryEntry ())); final ManifestProviderSupport supp = new ManifestProviderSupport (); cookies.add (supp); final EditorSupport es = new EditorSupport (getPrimaryEntry ()); //es.setMIMEType ("text/plain"); cookies.add (es); es.addChangeListener (WeakListener.change (chglist = new ChangeListener () { public void stateChanged (ChangeEvent ev) { //System.err.println("ManifestDataObject.es.stateChanged"); new Thread (new Runnable () { public void run () { Document doc = es.getDocument (); if (doc == null) { //System.err.println("\t(no doc)"); supp.fireStateChange (); return; } //System.err.println("\t(got doc)"); doc.addDocumentListener (WeakListener.document (doclist = new DocumentListener () { public void insertUpdate (DocumentEvent ev) { //System.err.println("ManifestDataObject.doc.insertUpdate"); update (); } public void removeUpdate (DocumentEvent ev) { //System.err.println("ManifestDataObject.doc.removeUpdate"); update (); } public void changedUpdate (DocumentEvent ev) { } private void update () { supp.fireStateChange (); } }, doc)); } }, "update doc listener for ManifestDataObject").start (); } }, es)); } public HelpCtx getHelpCtx () { return new HelpCtx ("org.netbeans.modules.apisupport.modules"); } protected Node createNodeDelegate () { return new ManifestDataNode (this); } private class ManifestProviderSupport implements ManifestProvider { /** * @associates ChangeListener */ private Set listeners = new HashSet (); // Set<ChangeListener> private Exception parseException = null; private boolean inited = false; // [PENDING] UTF8 encoding is untested, JRE does not yet support it anyway public synchronized Manifest getManifest () throws IOException { final StyledDocument doc = ((EditorCookie) getCookie (EditorCookie.class)).getDocument (); InputStream is; if (doc != null) { //System.err.println("ManifestDataObject.supp.getManifest; doc found"); final String[] text = new String[] { null }; try { final BadLocationException[] ble = new BadLocationException[] { null }; // [PENDING] could just use render() here NbDocument.runAtomicAsUser (doc, new Runnable () { public void run () { try { text[0] = doc.getText (0, doc.getLength ()); } catch (BadLocationException ble2) { ble[0] = ble2; } } }); if (ble[0] != null) throw ble[0]; } catch (BadLocationException ble3) { ble3.printStackTrace (); throw new IOException (ble3.toString ()); } is = new ByteArrayInputStream (text[0].getBytes ("UTF8")); } else { //System.err.println("ManifestDataObject.supp.getManifest; doc not found"); is = getPrimaryFile ().getInputStream (); } try { Manifest mani = new Manifest (is); parseException = null; return mani; } catch (IOException ioe) { parseException = ioe; return new Manifest (); } finally { is.close (); inited = true; } } public synchronized void setManifest (final Manifest m) throws IOException { // [PENDING] JRE bug workaround if (m.getMainAttributes ().getValue ("Manifest-Version") == null) { m.getMainAttributes ().putValue ("Manifest-Version", "1.0"); } final StyledDocument doc = ((EditorCookie) getCookie (EditorCookie.class)).openDocument (); try { final BadLocationException[] ble = new BadLocationException[] { null }; final IOException[] ioe = new IOException[] { null }; NbDocument.runAtomicAsUser (doc, new Runnable () { public void run () { try { doc.remove (0, doc.getLength ()); ByteArrayOutputStream baos = new ByteArrayOutputStream (); try { m.write (new StripCarriagesStream (baos)); } finally { baos.close (); } doc.insertString (0, baos.toString ("UTF8"), null); } catch (BadLocationException ble2) { ble[0] = ble2; } catch (IOException ioe2) { ioe[0] = ioe2; } } }); if (ble[0] != null) throw ble[0]; if (ioe[0] != null) throw ioe[0]; } catch (BadLocationException ble3) { ble3.printStackTrace (); throw new IOException (ble3.toString ()); } // Note: file is not saved at this point, that is up to the user. parseException = null; inited = true; } public void addFiles (Set files) throws IOException { } public void removeFiles (Set files) throws IOException { } public Set getFiles () throws IOException { return Collections.EMPTY_SET; } public void addChangeListener (ChangeListener list) { synchronized (listeners) { listeners.add (list); } } public void removeChangeListener (ChangeListener list) { synchronized (listeners) { listeners.remove (list); } } void fireStateChange () { SwingUtilities.invokeLater (new Runnable () { public void run () { //System.err.println("ManifestDataObject.supp.fireStateChange"); ChangeEvent ev = new ChangeEvent (this); Set _listeners; synchronized (listeners) { _listeners = new HashSet (listeners); } Iterator it = _listeners.iterator (); while (it.hasNext ()) ((ChangeListener) it.next ()).stateChanged (ev); } }); } private synchronized void init () { try { getManifest (); } catch (IOException ioe) { // OK to ignore here } } public boolean isValid () { init (); return parseException == null; } public Exception getParseException () { return parseException; } public File getManifestAsFile() { return NbClassPath.toFile (getPrimaryFile ()); } } private static class StripCarriagesStream extends FilterOutputStream { // [PENDING] will break with UTF8 public StripCarriagesStream (OutputStream os) { super (os); } public void write (int b) throws IOException { if (b != '\r') super.write (b); } public void write (byte[] b) throws IOException { write (b, 0, b.length); } public void write (byte[] b, int off, int len) throws IOException { for (int i = off; i < off + len; i++) if (b[i] != '\r') super.write (b[i]); } } } /* * Log * 6 Gandalf-post-FCS1.3.1.1 4/16/00 Jesse Glick Hopefully avoiding a * deadlock after reloading a manifest file, and some other * threading-related stuff. * 5 Gandalf-post-FCS1.3.1.0 3/28/00 Jesse Glick More robust module * install executor. * 4 Gandalf 1.3 2/4/00 Jesse Glick * 3 Gandalf 1.2 1/26/00 Jesse Glick Live manifest parsing. * 2 Gandalf 1.1 1/26/00 Jesse Glick Manifest handling * changed--now more dynamic, synched properly with open document as for * real file types. * 1 Gandalf 1.0 1/22/00 Jesse Glick * $ */